package com.yogurt.mp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yogurt.mp.OrderCreateRequest;
import com.yogurt.mp.entity.User;
import com.yogurt.mp.exception.UserErrorCode;
import com.yogurt.mp.exception.UserException;
import com.yogurt.mp.lock.DistributeLock;
import com.yogurt.mp.mapper.UserMapper;
import com.yogurt.mp.response.user.UserInfo;
import com.yogurt.mp.response.user.UserOperatorResponse;
import com.yogurt.mp.service.UserService;
import jakarta.validation.Valid;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
import java.util.List;

import static com.yogurt.mp.exception.UserErrorCode.DUPLICATE_TELEPHONE_NUMBER;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService, InitializingBean {

    private static final String DEFAULT_NICK_NAME_PREFIX = "藏家_";

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private DataSource dataSource;

    private RBloomFilter<String> nickNameBloomFilter;

    private RBloomFilter<String> inviteCodeBloomFilter;

    private RScoredSortedSet<String> inviteRank;

    @DistributeLock(keyExpression = "#telephone", scene = "USER_REGISTER")
    @Override
    public UserOperatorResponse register(String telephone, String inviteCode) {
        String defaultNickName;
        String randomString;
        do {
            randomString = RandomUtil.randomString(6).toUpperCase();
            //前缀 + 6位随机数 + 手机号后四位
            defaultNickName = DEFAULT_NICK_NAME_PREFIX + randomString + telephone.substring(7, 11);
        } while (nickNameExist(defaultNickName) & inviteCodeExist(randomString));

        String inviterId = null;
        if (StringUtils.isNotBlank(inviteCode)) {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("invite_code", inviteCode);
            User inviter = userMapper.selectOne(wrapper);
            if (inviter != null) {
                inviterId = inviter.getId().toString();
            }
        }

        User user = register(telephone, defaultNickName, telephone, randomString, inviterId);
        Assert.notNull(user, UserErrorCode.USER_OPERATE_FAILED.getCode());

        addNickName(defaultNickName);
        addInviteCode(randomString);
        updateInviteRank(inviterId);

        UserOperatorResponse userOperatorResponse = new UserOperatorResponse();
        userOperatorResponse.setSuccess(true);
        UserInfo userInfo = new UserInfo();
        BeanUtil.copyProperties(user, userInfo);
        userOperatorResponse.setUser(userInfo);
        return userOperatorResponse;
    }

    
    @Override
    public List<User> selectUserList() {
        List<User> users = userMapper.selectList(null);
        System.out.println("datasource: " + dataSource);
        Object resource = TransactionSynchronizationManager.getResource(dataSource);
        System.out.println("resource: " + resource);
        return users;
    }

    @Override
    public User selectUserByNickName(String nickName) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getNickName, nickName);
        return userMapper.selectOne(wrapper);
    }

    private User register(String telephone, String nickName, String password, String inviteCode, String inviterId) {
        if (userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getTelephone, telephone)) != null) {
            throw new UserException(DUPLICATE_TELEPHONE_NUMBER);
        }

        User user = new User();
        user.register(telephone, nickName, password, inviteCode, inviterId);
        if (save(user)) {
            return userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getTelephone,telephone));
        }
        return null;
    }

    public boolean nickNameExist(String nickName) {
        //如果布隆过滤器中存在，再进行数据库二次判断
        if (this.nickNameBloomFilter != null && this.nickNameBloomFilter.contains(nickName)) {
            return userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getNickName,nickName)) != null;
        }

        return false;
    }

    public boolean inviteCodeExist(String inviteCode) {
        //如果布隆过滤器中存在，再进行数据库二次判断
        if (this.inviteCodeBloomFilter != null && this.inviteCodeBloomFilter.contains(inviteCode)) {
            return userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getInviteCode,inviteCode)) != null;
        }

        return false;
    }

    private boolean addNickName(String nickName) {
        return this.nickNameBloomFilter != null && this.nickNameBloomFilter.add(nickName);
    }

    private boolean addInviteCode(String inviteCode) {
        return this.inviteCodeBloomFilter != null && this.inviteCodeBloomFilter.add(inviteCode);
    }

    private void updateInviteRank(String inviterId) {
        if (inviterId == null) {
            return;
        }
        RLock rLock = redissonClient.getLock(inviterId);
        rLock.lock();
        try {
            Double score = inviteRank.getScore(inviterId);
            if (score == null) {
                score = 0.0;
            }
            inviteRank.add(score + 100.0, inviterId);
        } finally {
            rLock.unlock();
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.nickNameBloomFilter = redissonClient.getBloomFilter("nickName");
        if (nickNameBloomFilter != null && !nickNameBloomFilter.isExists()) {
            this.nickNameBloomFilter.tryInit(100000L,0.01);
        }

        this.inviteCodeBloomFilter = redissonClient.getBloomFilter("inviteCode");
        if (inviteCodeBloomFilter != null && !inviteCodeBloomFilter.isExists()) {
            this.inviteCodeBloomFilter.tryInit(100000L, 0.01);
        }

        this.inviteRank = redissonClient.getScoredSortedSet("inviteRank");
    }

    @DistributeLock(keyExpression = "#orderCreateRequest.orderId1", scene = "NORMAL_BUY_ORDER")
    public void a(OrderCreateRequest orderCreateRequest) {
        System.err.println(orderCreateRequest.getOrderId1());
    }
}
